package msu.ml.data;

import msu.ml.core.*;
import msu.ml.util.*;
import java.io.*;
import java.util.*;
import org.w3c.dom.*;


public class CacheDataLoader implements IDataLoader
{
    private String baseDir;
    private HashMap<String, Long> manifest;

    public CacheDataLoader()
    {
        this.baseDir = ".cache";
        File dir = new File(this.baseDir);
        dir.mkdir();

        manifest = new HashMap<String, Long>();
        //read manifest from file
        try
        {
            File manifestFile = new File(this.baseDir + File.separatorChar + "manifest.xml");
            if(manifestFile.exists())
            {
                Document doc = XmlUtility.parseXmlFile(manifestFile);
                NodeList entries = XmlUtility.xPathQuery(doc, "//entry");
                for(int i = 0; i < entries.getLength(); i++)
                {
                    Element element = ((Element)entries.item(i));
                    if(element.getAttribute("type").equals("nx_instances"))
                        manifest.put(element.getAttribute("key"), 
                                Long.parseLong(element.getAttribute("accessed")));
                }
            }
        }
        catch(Exception e)
        {
            System.out.println(e);
				e.printStackTrace();
        }
    }

    public void clear()
    {
        Iterator<String> itr = manifest.keySet().iterator();
        while(itr.hasNext())
        {
            try
            {
                String key = itr.next();
                File file = new File(this.baseDir + File.separatorChar + key + ".cache");
                file.delete();
            }
            catch(Exception e)
            {
                System.out.println(e.getMessage());
                System.out.println(e.getStackTrace());
            }
        }
        manifest.clear();
        synch();
    }

	public boolean isSupportedData(String key)
    {
        return manifest.containsKey(key);
    }

	/**
	 * Load the file and return an NxInstances representation of it.
	 * 
	 * @param key file to load
	 * @return the NxInstances loaded from file
	 */
	public NxInstances load(String key)
    {
        if(!isSupportedData(key))
            return null;

        try
        {
            ObjectInputStream sin = new ObjectInputStream(
                    new FileInputStream(this.baseDir + File.separatorChar + key + ".cache"));
            NxInstances data = (NxInstances)sin.readObject();
            manifest.put(key, new Long(System.currentTimeMillis()));
            return data;
        }
        catch(Exception e) 
        {
            System.out.println(e.getMessage());
            System.out.println(e.getStackTrace());
        }
        return null;
    }

    public void store(NxInstances data, String key)
    {
        try
        {
            ObjectOutputStream sout = new ObjectOutputStream(
                    new FileOutputStream(this.baseDir + File.separatorChar + key + ".cache"));
            sout.writeObject(data);
            sout.close();
            manifest.put(key, new Long(System.currentTimeMillis()));
        }
        catch(Exception e) 
        {
            System.out.println(e.getMessage());
            System.out.println(e.getStackTrace());
        }
    }

    public void synch()
    {
        try
        {
            PrintWriter sout = new PrintWriter(this.baseDir + 
                File.separatorChar + "manifest.xml");
            sout.println("<?xml version = \"1.0\"?>");
            sout.println("<cache_manifest>");
            Iterator<String> itr = manifest.keySet().iterator();
            while(itr.hasNext())
            {
                String key = itr.next();
                sout.println("<entry type=\"nx_instances\" key=\"" +
                    key + "\" accessed=\"" + manifest.get(key) + "\" />");
            }
            sout.println("</cache_manifest>");
            sout.close();
        }
        catch(Exception e) 
        {
            System.out.println(e.getMessage());
            System.out.println(e.getStackTrace());
        }
    }

    protected void finalize() throws Throwable
    {
        try { synch(); }
        finally { super.finalize(); }
    }

}
